﻿

//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpAssert.h"
#include "LogManager.h"

#include <QComboBox>
#include <QStyleFactory>
#include <QtGui/qevent.h>
#include "CrashHelperEx.h"
#include "qtpropertymanager.h"
#include "qtvariantproperty.h"
#include "qttreepropertybrowser.h"
#include "PropertyInstanceWidget.h"
#include "QBoxLayout"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"


using namespace Sample;

CInstancePropertyWidget::CInstancePropertyWidget(int nWidth, int nHeight, 
    CPropertyInstanceSetting *pPropertyInstanceSetting, QWidget *pParent/* = 0*/) 
    : QFrame(pParent)
    , m_propertyInstanceSetting(pPropertyInstanceSetting)
    , m_variantManager(nullptr)
    , m_variantFactory(nullptr)
    , m_propertyBrowser(nullptr)
{
    resize(nWidth, nHeight);
    
    m_variantManager = std::unique_ptr<QtVariantPropertyManager>(new QtVariantPropertyManager());
    m_variantFactory = std::unique_ptr<QtVariantEditorFactory>(new QtVariantEditorFactory());
    m_propertyBrowser = std::unique_ptr<QtTreePropertyBrowser>(new QtTreePropertyBrowser(this));
    m_propertyBrowser->treeWidget()->installEventFilter(this);
    m_propertyBrowser->setFactoryForManager(m_variantManager.get(), m_variantFactory.get());
    m_propertyBrowser->setAlternatingRowColors(false);
    m_propertyBrowser->setPropertiesWithoutValueMarked(true);
    m_propertyBrowser->setRootIsDecorated(false);
    m_propertyBrowser->setHeaderVisible(false);
    m_propertyBrowser->setIndentation(0);
    connect(m_propertyBrowser.get(), SIGNAL(sigBindButtonClicked(int)), this, SIGNAL(sigPropertyBindButtonClicked(int)), Qt::UniqueConnection);
    connect(m_propertyBrowser.get(), SIGNAL(sigEditButtonClicked(int)), this, SIGNAL(sigPropertyEditButtonClicked(int)), Qt::UniqueConnection);

    QVBoxLayout* pLayout = new QVBoxLayout(this);
    pLayout->setContentsMargins(0, 0, 0, 0); 
    pLayout->addWidget(m_propertyBrowser.get());

    //避免全局style影响导致（控件不能灰色显示、字体颜色灰暗等）
    setStyle(QStyleFactory::create("WindowsVista"));
}

CInstancePropertyWidget::~CInstancePropertyWidget()
{
}

void CInstancePropertyWidget::OnPropertyValueChanged(QtProperty *property, const QVariant &val)
{
    for(int ng = 0; ng < m_propertyInstanceSetting->GetGroupItemsCount(); ++ng)
    {
        CPropertyGroupItem* pGroup = m_propertyInstanceSetting->PropertyGroupItem(ng);
        for(int nItem = 0; nItem < pGroup->PropertyItemsCount(); ++nItem)
        {
            CPropertyItem* pItemData = pGroup->Item(nItem);
            if(pItemData->UniqueID() == property->uniqueID())
            {
                bool isChanged = false;
                GEditStyle editType = pItemData->GetEditStyle();
                if((editType == esDropDown || editType == esDropDownWindow || editType == esDropDownWindowEllipsis || editType == esDropDownWindowNone ||
                    editType == esColorList || editType == esFontList) && !pItemData->GetComboboxItems().empty())
                { 
                    pItemData->SetIsMultiValue(false);
                    if (val.type() == QVariant::Int)
                    {
                        int idx = val.toInt();
                        isChanged = (pItemData->GetComboboxItems()[idx] != pItemData->GetValue()) ? true : false;
                        pItemData->SetValue(pItemData->GetComboboxItems()[idx]);
                        emit DropDownListIndexChange(ng, nItem);
                    }
                    else if (val.type() == QVariant::String)
                    {
                        isChanged = (val != pItemData->GetValue());
                        pItemData->SetValue(val);
                    }
                }
                else 
                {
                    isChanged = pItemData->GetValue() != val ? true : false;
                    pItemData->SetValue(val);
                    pItemData->SetIsMultiValue(false);
                }

                if(isChanged)
                {
                    emit PropertyChanged(ng, nItem);
                }
                return;
            }
        }
    }
}

void CInstancePropertyWidget::ClearAllProperty()
{
    QList<QtProperty *> groupList = m_propertyBrowser->properties();
    for (auto itGroup = groupList.begin(); itGroup != groupList.end(); )
    {
        m_propertyBrowser->removeProperty((*itGroup));
        delete (*itGroup);
        itGroup = groupList.erase(itGroup);
    }
}

void CInstancePropertyWidget::Build( bool bRebuild /*= false*/ )
{
    if (!m_propertyInstanceSetting)
        return;

    if (bRebuild)
    {
        //清除原属性项目
        ClearAllProperty();
    }

    //用设置数据填充属性表数据
    disconnect(m_variantManager.get(), SIGNAL(valueChanged(QtProperty *, const QVariant &)), 
        this, SLOT(OnPropertyValueChanged(QtProperty *, const QVariant &)));

    QList<QtProperty *> groupList = m_propertyBrowser->properties();
    for(int ng = 0; ng < m_propertyInstanceSetting->GetGroupItemsCount(); ++ng)
    {
        CPropertyGroupItem* pGroup = m_propertyInstanceSetting->PropertyGroupItem(ng);
        // 检测pGroup所指对象的内存是否是有效可读的
        CrashHelperEx::CrashWarn(CrashHelperEx::IsValidAddress(pGroup), QString("pGroup[%1] is invalid").arg(QString::number(reinterpret_cast<INT_PTR>(pGroup), 16)));
        QtProperty * groupItem = nullptr;
        for(auto itrg = groupList.begin(); itrg != groupList.end(); ++itrg)
        {
            if((*itrg)->uniqueID() == pGroup->UniqueID())
            {
                groupItem = *itrg;
                break;
            }
        }

        //设置组信息到控件项, 如果为空组，则不创建组
        if(groupItem == nullptr && pGroup->PropertyItemsCount() > 0)
        {
            groupItem = m_variantManager->addProperty(QtVariantPropertyManager::groupTypeId(), pGroup->UniqueID(), pGroup->GetName());
            m_propertyBrowser->addProperty(groupItem);
        }

        if (groupItem == nullptr)
            continue;

        groupItem->setPropertyName(pGroup->GetName());
        groupItem->setToolTip(pGroup->GetDescription());
        groupItem->setStatusTip(pGroup->GetDescription());
     
        QList<QtProperty *> propertyList = groupItem->subProperties();
        QtVariantProperty * preItem = nullptr;
        for(int nItem = 0; nItem < pGroup->PropertyItemsCount(); ++nItem)
        {
            CPropertyItem* pItemData = pGroup->Item(nItem);
            QVariant::Type propertyType = QVariant::Invalid;
            QVariant propertyVal = pItemData->GetValue();

            GEditStyle style = pItemData->GetEditStyle();
            switch(style)
            {
            case esNone:
            case esSimple:
            case esPlainEdit:
            case esLineEdit:
                {
                    propertyType = QVariant::String;
                    break;
                }
            case esPlainEllipsis:
                {
                    break;
                }
            case esLineEllipsis:
                {
                    propertyType = (QVariant::Type)QtVariantPropertyManager::materialTypeId();
                    break;
                }
            case esMultiButton: break;
            case esBool: { propertyType = QVariant::Bool; break;} 
            case esMonthCalendar:{ propertyType = QVariant::DateTime; break;}// 日期选择框
            case esImage: { propertyType = QVariant::Image; break;}// 图片
            case esVectorGraph:{ propertyType = QVariant::Icon; break;} // 矢量图
            case esFontList:{ propertyType = QVariant::Font; break;} // 字体选择框
            case esColorList:{ propertyType = QVariant::Color; break;} // 颜色选择列表
            case esLineWidthList:{ propertyType = QVariant::List; break;} // 线框选择列表
            case esUpDown:{ propertyType = QVariant::Double; break;}  // 增量设置框
            case esDropDown:{ propertyType = (QVariant::Type)QtVariantPropertyManager::enumTypeId(); break;} // 下拉选择框
            case esTreeView: break;                // 树形框
            case esDropDownWindow: break;          // 下拉的浮动窗体
            case esDropDownWindowEllipsis: break;  // 下拉的浮动窗体, button的样式为三个点
            case esDropDownWindowNone: break;      // 下拉的浮动窗体, 不显示button
            default:
                break;
            }

            //设置属性项目信息到属性表
            QtVariantProperty *item = nullptr;
            for(auto itrp = propertyList.begin(); itrp != propertyList.end(); ++itrp)
            {
                if((*itrp)->uniqueID() == pItemData->UniqueID())
                {
                    item = dynamic_cast<QtVariantProperty *>(*itrp);
                    break;
                }
            }

            // add Property
            if (item == nullptr)
            {
                item = m_variantManager->addProperty(propertyType, pItemData->UniqueID(), pItemData->GetName());
            }
            if (!item)
            {
                GBMP_ASSERT(item);
                return;
            }

            // Set Value
            if(style == esDropDown)
            {
                item->setAttribute(QLatin1String("enumNames"), pItemData->GetComboboxItems());
                if (pItemData->IsManualInputModifiable())
                {
                    m_variantManager->setValue(item, propertyVal);
                }
                else
                {
                    int idx = pItemData->GetComboboxItems().indexOf(propertyVal.toString());
                    //GBMP_ASSERT(idx != -1); // 当前值并不在列表里面 TODO: 立面视图工作楼层问题
                    item->setValue(idx);
                }
            }
            else
            {
                m_variantManager->setValue(item, propertyVal);
            }

            // Set Name and State
            item->setPropertyName(pItemData->GetName());
            pItemData->IsEnable() ? item->setEnabled(true) : item->setEnabled(false);//设置灰显状态
            item->setStatusTip(pItemData->GetDescription());
            item->setToolTip(pItemData->GetDescription());
            item->setCanBind(pItemData->CanBind());
            item->setModified(pItemData->IsManualInputModifiable());

            //会自动过滤重复添加项
            groupItem->insertSubProperty(item, preItem);
            preItem = item;
        }
    }

    connect(m_variantManager.get(), SIGNAL(valueChanged(QtProperty *, const QVariant &)), 
        this, SLOT(OnPropertyValueChanged(QtProperty *, const QVariant &)));

    connect(m_variantManager.get(), SIGNAL(sigHidePopup(QObject*)),
        this, SIGNAL(sigHidePopup(QObject*)));
}

void CInstancePropertyWidget::RefreshView( bool bRebuild /*= false*/ )
{
    Build(bRebuild);
}

CPropertyInstanceSetting* CInstancePropertyWidget::GetPropertyInstanceSetting() const
{
    return m_propertyInstanceSetting.get();
}

CPropertyGroupItem* CInstancePropertyWidget::InsertPropertyGroup(int index, int uniqueID, const QString& name, 
    const QString& description)
{
    return m_propertyInstanceSetting->InsertPropertyGroupItem(index, uniqueID, name, description);
}

CPropertyGroupItem* CInstancePropertyWidget::AddPropertyGroup(int uniqueID, const QString& name, const QString& description)
{
    return m_propertyInstanceSetting->AddPropertyGroupItem(uniqueID, name, description);
}

CPropertyGroupItem* CInstancePropertyWidget::GetPropertyGroupByIndex(int index) const
{
    return m_propertyInstanceSetting->PropertyGroupItem(index);
}

CPropertyGroupItem* CInstancePropertyWidget::GetPropertyGroupByName(const QString& name) const
{
    return m_propertyInstanceSetting->PropertyGroupItem(name);
}

CPropertyGroupItem* CInstancePropertyWidget::GetPropertyGroupByUniqueID(int uniqueID) const
{
    return m_propertyInstanceSetting->GetPropertyGroupItemByUniqueID(uniqueID);
}

bool CInstancePropertyWidget::RemovePropertyGroup(int groupIndex)
{
    return m_propertyInstanceSetting->RemoveGroupItem(groupIndex);
}

bool CInstancePropertyWidget::RemoveAllPropertyGroup()
{
    ClearAllProperty();
    return m_propertyInstanceSetting->RemoveAllGroupItems();
}

int CInstancePropertyWidget::GetPropertyGroupCount() const
{
    return m_propertyInstanceSetting->GetGroupItemsCount();
}

CPropertyItem* CInstancePropertyWidget::InsertProperty( int groupIndex, int index, int uniqueID, const QString& name, 
    const QString& description, const QVariant& value, bool isEnable, GEditStyle editStyle, bool bCanbeBind/* = false*/)
{
    int propertyGroupCount = m_propertyInstanceSetting->GetGroupItemsCount();
    if (groupIndex < 0 || groupIndex >= propertyGroupCount)
    {
        return NULL;
    }
    CPropertyGroupItem *pPropertyGroup = m_propertyInstanceSetting->PropertyGroupItem(groupIndex);
    GBMP_ASSERT(pPropertyGroup);
    if (pPropertyGroup)
    {
        return pPropertyGroup->InsertPropertyItem(index, uniqueID, name, description, value, isEnable, editStyle, bCanbeBind);
    }
    return NULL;
}

CPropertyItem* CInstancePropertyWidget::AddProperty( int groupIndex, int uniqueID, const QString& name, const QString& description, 
    const QVariant& value, bool isEnable, GEditStyle editStyle , bool bCanbeBind/* = false*/)
{
    int propertyGroupCount = m_propertyInstanceSetting->GetGroupItemsCount();
    if (groupIndex < 0 || groupIndex >= propertyGroupCount)
    {
        return NULL;
    }
    CPropertyGroupItem *pPropertyGroup = m_propertyInstanceSetting->PropertyGroupItem(groupIndex);
    GBMP_ASSERT(pPropertyGroup);
    if (pPropertyGroup)
    {
        return pPropertyGroup->AddPropertyItem(uniqueID, name,description, value, isEnable, editStyle, bCanbeBind);
    }
    return NULL;
}

CPropertyItem* CInstancePropertyWidget::GetPropertyByIndex( int groupIndex, int nPropertyIndex ) const
{
    int propertyGroupCount = m_propertyInstanceSetting->GetGroupItemsCount();
    if (groupIndex < 0 || groupIndex >= propertyGroupCount)
    {
        return NULL;
    }
    CPropertyGroupItem *pPropertyGroup = m_propertyInstanceSetting->PropertyGroupItem(groupIndex);
    GBMP_ASSERT(pPropertyGroup);
    if (pPropertyGroup)
    {
        return pPropertyGroup->Item(nPropertyIndex);
    }
    return NULL;
}

CPropertyItem* CInstancePropertyWidget::GetPropertyByName( int groupIndex, const QString& name ) const
{
    int propertyGroupCount = m_propertyInstanceSetting->GetGroupItemsCount();
    if (groupIndex < 0 || groupIndex >= propertyGroupCount)
    {
        return NULL;
    }
    CPropertyGroupItem *pPropertyGroup = m_propertyInstanceSetting->PropertyGroupItem(groupIndex);
    GBMP_ASSERT(pPropertyGroup);
    if (pPropertyGroup)
    {
        return pPropertyGroup->Item(name);
    }
    return NULL;
}

CPropertyItem* CInstancePropertyWidget::GetPropertyByUniqueID(int uniqueID) const
{
    return m_propertyInstanceSetting->GetPropertyItemByUniqueID(uniqueID);
}

bool CInstancePropertyWidget::RemoveProperty( int groupIndex, int nPropertyIndex )
{
    int propertyGroupCount = m_propertyInstanceSetting->GetGroupItemsCount();
    if (groupIndex < 0 || groupIndex >= propertyGroupCount)
    {
        return false;
    }
    CPropertyGroupItem *pPropertyGroup = m_propertyInstanceSetting->PropertyGroupItem(groupIndex);
    GBMP_ASSERT(pPropertyGroup);
    if (pPropertyGroup)
    {
        return pPropertyGroup->RemoveItem(nPropertyIndex);
    }
    return false;
}

bool CInstancePropertyWidget::RemoveProperty( int groupIndex, const QString& name )
{
    int propertyGroupCount = m_propertyInstanceSetting->GetGroupItemsCount();
    if (groupIndex < 0 || groupIndex >= propertyGroupCount)
    {
        return false;
    }
    CPropertyGroupItem *pPropertyGroup = m_propertyInstanceSetting->PropertyGroupItem(groupIndex);
    GBMP_ASSERT(pPropertyGroup);
    if (pPropertyGroup)
    {
        return pPropertyGroup->RemoveItem(name);
    }
    return false;
}

void CInstancePropertyWidget::RemoveAllPropertys( int groupIndex )
{
    int propertyGroupCount = m_propertyInstanceSetting->GetGroupItemsCount();
    if (groupIndex < 0 || groupIndex >= propertyGroupCount)
    {
        return;
    }
    CPropertyGroupItem *pPropertyGroup = m_propertyInstanceSetting->PropertyGroupItem(groupIndex);
    GBMP_ASSERT(pPropertyGroup);
    if (pPropertyGroup)
    {
        pPropertyGroup->RemoveAllPropertyItems();
    }
}

int CInstancePropertyWidget::GetPropertyCount(int groupIndex) const
{
    CPropertyGroupItem *pPropertyGroup = m_propertyInstanceSetting->PropertyGroupItem(groupIndex);
    GBMP_ASSERT(pPropertyGroup);
    if (pPropertyGroup)
        return pPropertyGroup->PropertyItemsCount();
    return 0;
}

void CInstancePropertyWidget::OnMouseLeave() 
{
    //取消当前编辑项目焦点
    m_propertyBrowser->setCurrentItem(nullptr);
}

//事件过滤器，用于转发属性表控件事件到父窗口处理
bool CInstancePropertyWidget::eventFilter(QObject *pObject, QEvent *pEvent)
{
    if(pObject == m_propertyBrowser->treeWidget() && pEvent->type() == QEvent::KeyPress )
    {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(pEvent);
        if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) 
        {
            //取消当前编辑项目焦点
            m_propertyBrowser->setCurrentItem(nullptr);
            setFocus();
            emit FinishedEditing(); 
        }
    }
    return QFrame::eventFilter(pObject, pEvent);
}

void CInstancePropertyWidget::SetBindButtonColomnVisible(bool bVisible)
{
    m_bButtonColomnShow = bVisible;
    m_propertyBrowser->treeWidget()->setColumnHidden(2, !bVisible);
}

void CInstancePropertyWidget::SetTreePropertyBrowserName(const QString& name)
{
    m_propertyBrowser->setObjectName(name);
}

QtPropertyEditorView* CInstancePropertyWidget::GetPropertyEditorView() const
{
    if (m_propertyBrowser)
    {
        return m_propertyBrowser->treeWidget();
    }

    return nullptr;
}
